/*
* Copyright (c) Reveal Technologies, LLC. All rights reserved. http://www.reveal-tech.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package com.sitewhere.assetmodule.magento;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.xml.ws.soap.SOAPFaultException;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.log4j.Logger;
import com.sitewhere.assetmodule.magento.ws.ArrayOfString;
import com.sitewhere.assetmodule.magento.ws.AssociativeArray;
import com.sitewhere.assetmodule.magento.ws.AssociativeEntity;
import com.sitewhere.assetmodule.magento.ws.CatalogAttributeEntity;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeListRequestParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeListResponseParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeMediaListRequestParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeMediaListResponseParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeSetEntity;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeSetListRequestParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductAttributeSetListResponseParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductEntity;
import com.sitewhere.assetmodule.magento.ws.CatalogProductImageEntity;
import com.sitewhere.assetmodule.magento.ws.CatalogProductInfoRequestParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductInfoResponseParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductListRequestParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductListResponseParam;
import com.sitewhere.assetmodule.magento.ws.CatalogProductRequestAttributes;
import com.sitewhere.assetmodule.magento.ws.CatalogProductReturnEntity;
import com.sitewhere.assetmodule.magento.ws.LoginParam;
import com.sitewhere.assetmodule.magento.ws.LoginResponseParam;
import com.sitewhere.assetmodule.magento.ws.MageApiModelServerWsiHandlerPortType;
import com.sitewhere.assetmodule.magento.ws.MagentoService;
import com.sitewhere.rest.model.asset.HardwareAsset;
import com.sitewhere.rest.model.command.CommandResponse;
import com.sitewhere.server.asset.AssetMatcher;
import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.asset.AssetType;
import com.sitewhere.spi.asset.IAssetModule;
import com.sitewhere.spi.command.CommandResult;
import com.sitewhere.spi.command.ICommandResponse;
/**
* Asset module that interacts with an external Magento server.
*
* @author dadams
*/
public class MagentoAssetModule implements IAssetModule<HardwareAsset> {
/** Static logger instance */
private static Logger LOGGER = Logger.getLogger(MagentoAssetModule.class);
/** Module id */
private static final String MODULE_ID = "magento";
/** Module name */
private static final String MODULE_NAME = "Magento Asset Module";
/** Default base URL for Magento SOAP v2 web service */
private static final String DEFAULT_URL = "http://locahost/magento/index.php/api/v2_soap?wsdl";
/** Default Magento username */
private static final String DEFAULT_USERNAME = "magento";
/** Default Magento password */
private static final String DEFAULT_PASSWORD = "magento";
/** Number of threads used to make calls to Magento */
private static final int DEFAULT_THREAD_POOL_SIZE = 5;
/** Base URL for Magento SOAP v2 web service */
private String magentoUrl = DEFAULT_URL;
/** Username used to log in to Magento */
private String magentoUsername = DEFAULT_USERNAME;
/** Password used to log in to Magento */
private String magentoPassword = DEFAULT_PASSWORD;
/** Indicates whether SOAP messages should be sent to the log */
private boolean debugSoap = false;
/** Cached asset map */
private Map<String, HardwareAsset> assetCache = new HashMap<String, HardwareAsset>();
/** Matcher used for searches */
protected AssetMatcher matcher = new AssetMatcher();
/** Magento web service */
protected MagentoService magento;
/** Port for accessing Magento service */
protected MageApiModelServerWsiHandlerPortType port;
/** Unique session id */
protected String sessionId;
/** Map of attribute sets by unique id */
private Map<Integer, ArrayOfString> attributeSets = new HashMap<Integer, ArrayOfString>();
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#start()
*/
public void start() throws SiteWhereException {
LOGGER.info("Connecting to Magento web service as user '" + getMagentoUsername() + "'.");
try {
magento = new MagentoService(new URL(getMagentoUrl()));
port = magento.getMageApiModelServerWsiHandlerPort();
if (isDebugSoap()) {
Client client = ClientProxy.getClient(port);
client.getInInterceptors().add(new LoggingInInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
}
login();
cacheAssetData();
} catch (MalformedURLException e) {
throw new SiteWhereException(e);
}
}
/**
* Logs in to Magento and saves the session id.
*
* @throws SiteWhereException
*/
protected void login() throws SiteWhereException {
LoginParam loginParams = new LoginParam();
loginParams.setUsername(getMagentoUsername());
loginParams.setApiKey(getMagentoPassword());
try {
LoginResponseParam loginResponse = port.login(loginParams);
sessionId = loginResponse.getResult();
} catch (SOAPFaultException e) {
throw new SiteWhereException("Magento login failed.", e);
}
}
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#stop()
*/
public void stop() throws SiteWhereException {
}
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#getId()
*/
public String getId() {
return MODULE_ID;
}
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#getName()
*/
public String getName() {
return MODULE_NAME;
}
/*
* (non-Javadoc)
*
* @see
* com.sitewhere.spi.asset.IAssetModule#isAssetTypeSupported(com.sitewhere.spi.asset
* .AssetType)
*/
public boolean isAssetTypeSupported(AssetType type) {
if (type == AssetType.Hardware) {
return true;
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.sitewhere.spi.asset.IAssetModule#getAssetById(com.sitewhere.spi.asset.AssetType
* , java.lang.String)
*/
public HardwareAsset getAssetById(AssetType type, String id) throws SiteWhereException {
return assetCache.get(id);
}
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#search(com.sitewhere.spi.asset.AssetType,
* java.lang.String)
*/
public List<HardwareAsset> search(AssetType type, String criteria) throws SiteWhereException {
criteria = criteria.toLowerCase();
List<HardwareAsset> results = new ArrayList<HardwareAsset>();
if (criteria.length() == 0) {
results.addAll(assetCache.values());
return results;
}
for (HardwareAsset asset : assetCache.values()) {
if (matcher.isHardwareMatch(asset, criteria)) {
results.add(asset);
}
}
return results;
}
/*
* (non-Javadoc)
*
* @see com.sitewhere.spi.asset.IAssetModule#refresh()
*/
public ICommandResponse refresh() throws SiteWhereException {
try {
return cacheAssetData();
} catch (SiteWhereException e) {
return new CommandResponse(CommandResult.Failed, e.getMessage());
}
}
/**
* Make calls to the Magento server to get all products and cache them locally for
* fast searches.
*
* @throws SiteWhereException
*/
protected ICommandResponse cacheAssetData() throws SiteWhereException {
assetCache.clear();
attributeSets.clear();
LOGGER.info("Caching search data.");
int totalAssets = 0;
long startTime = System.currentTimeMillis();
// Get attribute set list using multiple threads.
ExecutorService attrExec = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
CatalogProductAttributeSetListRequestParam aslReq = new CatalogProductAttributeSetListRequestParam();
aslReq.setSessionId(sessionId);
CatalogProductAttributeSetListResponseParam aslResp = port.catalogProductAttributeSetList(aslReq);
List<Future<ArrayOfString>> attrResults = new ArrayList<Future<ArrayOfString>>();
for (CatalogProductAttributeSetEntity set : aslResp.getResult().getComplexObjectArray()) {
attrResults.add(attrExec.submit(new AttributeSetLoader(set.getSetId())));
}
for (Future<ArrayOfString> attrResult : attrResults) {
try {
attrResult.get();
} catch (Throwable e) {
throw new SiteWhereException(e);
}
}
// Load product list using multiple threads.
ExecutorService prodExec = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
CatalogProductListRequestParam listReq = new CatalogProductListRequestParam();
listReq.setSessionId(sessionId);
CatalogProductListResponseParam listResp = port.catalogProductList(listReq);
List<CatalogProductEntity> products = listResp.getResult().getComplexObjectArray();
List<Future<HardwareAsset>> prodResults = new ArrayList<Future<HardwareAsset>>();
for (CatalogProductEntity product : products) {
prodResults.add(prodExec.submit(new ProductLoader(product)));
}
for (Future<HardwareAsset> prodResult : prodResults) {
try {
prodResult.get();
totalAssets++;
} catch (Throwable e) {
throw new SiteWhereException(e);
}
}
long totalTime = System.currentTimeMillis() - startTime;
String message = "Cached " + totalAssets + " assets in " + totalTime + "ms.";
LOGGER.info(message);
return new CommandResponse(CommandResult.Successful, message);
}
public String getMagentoUrl() {
return magentoUrl;
}
public void setMagentoUrl(String magentoUrl) {
this.magentoUrl = magentoUrl;
}
public String getMagentoUsername() {
return magentoUsername;
}
public void setMagentoUsername(String magentoUsername) {
this.magentoUsername = magentoUsername;
}
public String getMagentoPassword() {
return magentoPassword;
}
public void setMagentoPassword(String magentoPassword) {
this.magentoPassword = magentoPassword;
}
public boolean isDebugSoap() {
return debugSoap;
}
public void setDebugSoap(boolean debugSoap) {
this.debugSoap = debugSoap;
}
/**
* Loads attribute sets in a separate thread.
*
* @author Derek
*/
protected class AttributeSetLoader implements Callable<ArrayOfString> {
/** Attribute set id */
private Integer setId;
public AttributeSetLoader(Integer setId) {
this.setId = setId;
}
/*
* (non-Javadoc)
*
* @see java.util.concurrent.Callable#call()
*/
public ArrayOfString call() throws Exception {
CatalogProductAttributeListRequestParam request = new CatalogProductAttributeListRequestParam();
request.setSessionId(sessionId);
request.setSetId(setId);
CatalogProductAttributeListResponseParam response = port.catalogProductAttributeList(request);
List<CatalogAttributeEntity> entities = response.getResult().getComplexObjectArray();
ArrayOfString converted = new ArrayOfString();
for (CatalogAttributeEntity entity : entities) {
converted.getComplexObjectArray().add(entity.getCode());
}
attributeSets.put(setId, converted);
return converted;
}
}
/**
* Loads product information from Magento in a separate thread.
*
* @author Derek
*/
protected class ProductLoader implements Callable<HardwareAsset> {
/** Unique product id */
private CatalogProductEntity product;
public ProductLoader(CatalogProductEntity product) {
this.product = product;
}
/*
* (non-Javadoc)
*
* @see java.util.concurrent.Callable#call()
*/
public HardwareAsset call() throws Exception {
HardwareAsset asset = new HardwareAsset();
Integer setId = Integer.parseInt(product.getSet());
ArrayOfString attrSet = attributeSets.get(setId);
// Load product attribute details.
CatalogProductRequestAttributes attr = new CatalogProductRequestAttributes();
attr.setAttributes(new ArrayOfString());
attr.setAdditionalAttributes(attrSet);
CatalogProductInfoRequestParam infoRequest = new CatalogProductInfoRequestParam();
infoRequest.setSessionId(sessionId);
infoRequest.setProductId(product.getProductId());
infoRequest.setAttributes(attr);
CatalogProductInfoResponseParam infoResponse = port.catalogProductInfo(infoRequest);
CatalogProductReturnEntity entity = infoResponse.getResult();
// Load all attributes.
AssociativeArray addAttr = entity.getAdditionalAttributes();
if (addAttr != null) {
for (AssociativeEntity assoc : addAttr.getComplexObjectArray()) {
asset.setProperty(assoc.getKey(), assoc.getValue());
}
}
asset.setId(entity.getProductId());
asset.setSku(entity.getSku());
asset.setName(asset.getProperty(IMagentoFields.PROP_NAME));
asset.setDescription(asset.getProperty(IMagentoFields.PROP_DESCRIPTION));
// Load product image data.
CatalogProductAttributeMediaListRequestParam mediaReq = new CatalogProductAttributeMediaListRequestParam();
mediaReq.setSessionId(sessionId);
mediaReq.setProductId(product.getProductId());
CatalogProductAttributeMediaListResponseParam mediaResp = port
.catalogProductAttributeMediaList(mediaReq);
List<CatalogProductImageEntity> mediaList = mediaResp.getResult().getComplexObjectArray();
for (CatalogProductImageEntity media : mediaList) {
asset.setImageUrl(media.getUrl());
break;
}
assetCache.put(asset.getId(), asset);
return asset;
}
}
}